import {
  CancelExecutor,
  CancelToken as CancelTokenInterface,
  CancelTokenSource,
  Canceler,
} from "../types";
import Cancel from "./Cancel";

interface ResolvePromise {
  (reason?: Cancel): void;
}

export default class CancelToken implements CancelTokenInterface {
  promise: Promise<Cancel>;
  reason?: Cancel;

  constructor(executor: CancelExecutor) {
    let resolvePromise: ResolvePromise;
    this.promise = new Promise<Cancel>((resolve) => (resolvePromise = resolve));

    executor((message) => {
      if (this.reason) return;

      this.reason = new Cancel(message);
      resolvePromise(this.reason);
    });
  }

  static source(): CancelTokenSource {
    let cancel!: Canceler;
    const token = new CancelToken((c) => (cancel = c));
    return {
      cancel,
      token,
    };
  }

  throwIfRequested(): void {
    if (this.reason) {
      throw this.reason;
    }
  }
}
